package com.xuecheng.base.exception;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

/**
 * @description 全局异常处理器
 * @version 1.0
 */
@Slf4j
@ControllerAdvice
//@RestControllerAdvice  表示@ControllerAdvice + @ResponseBody
public class GlobalExceptionHandler {
    //返回json
    @ResponseBody
    @ExceptionHandler(XueChengPlusException.class)
    //响应给前端的状态码 INTERNAL_SERVER_ERROR表示服务端内部错误
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    //流程是在代码里面写入 XueChengPlusException.cast("课程名称为空"); cast里面构造了XueChengPlusException把"课程名称为空"信息写入了errmessage里面
    //然后 @ExceptionHandler(XueChengPlusException.class)进行捕获 在这里把errmessage取出来放到RestErrorResponse里面返回给前端
    public RestErrorResponse customException(XueChengPlusException e) {
        //记录异常日志
        log.error("【系统异常】{}",e.getErrMessage(),e);
        //和前端约定好把错误的信息放在RestErrorResponse里面的errMessage属性
        //好让在数据返回给前端的时候从这个属性里面取出错误的信息然后展示在页面上
        //进行有参构造 构造出RestErrorResponse把RestErrorResponse返回给前端 前端根据RestErrorResponse对象可以把里面的错误信息ErrMessage取出来
        return new RestErrorResponse(e.getErrMessage());

    }

    //
    @ResponseBody
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public RestErrorResponse exception(Exception e) {

        log.error("【系统异常】{}",e.getMessage(),e);
        //如果直接捕获springSecurity的异常需要引入依赖 则由于base全部工程都依赖了这个包 如果base引入了springSecurity依赖则全部包都引入了 没有必要
        // 所以可以在总的异常处理器哪里进行判断异常的信息 然后捕获报出友好的提示信息
        //这是RBAC模块加的
        if(e.getMessage().equals("不允许访问")){
            return new RestErrorResponse("没有操作此功能的权限");
        }
        //自定义的异常抛出给前端的信息是由我们自己输入的如XueChengPlusException.cast("课程名称为空")
        //这里捕获的是其他异常（系统抛出的异常） 可以抛出CommonError.UNKOWN_ERROR.getErrMessage()这个统一的错误信息给前端进行展示
        return new RestErrorResponse(CommonError.UNKOWN_ERROR.getErrMessage());

    }

    ////这里捕获的是JSR303统一校验的Validated抛出的异常
    @ResponseBody
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public RestErrorResponse methodArgumentNotValidException(MethodArgumentNotValidException e) {

        //这里捕获的是JSR303统一校验的Validated抛出的异常 在进行前端参数的校验的时候也加上了message错误信息 所以异常处理器可以拿到这个自己定义的错误信息对前端进行展示
        BindingResult bindingResult = e.getBindingResult();
        List<String> msgList = new ArrayList<>();
        // getFieldErrors是一个集合异常都放在这里面 采用stream流解析异常
        // 将错误信息放在msgList（因为一个属性上面可能加了多个校验的规则 所以错误信息可能存在多条 所以是一个list）
        bindingResult.getFieldErrors().stream().forEach(item->msgList.add(item.getDefaultMessage()));
        //拼接错误信息  把一个list集合进行以逗号分隔进行拼接成字符串
        String msg = StringUtils.join(msgList, ",");
        log.error("【系统异常】{}",msg);
        return new RestErrorResponse(msg);

    }
}
